/*****************************************************************************
 *   Copyright(C)2009-2022 by VSF Team                                       *
 *                                                                           *
 *  Licensed under the Apache License, Version 2.0 (the "License");          *
 *  you may not use this file except in compliance with the License.         *
 *  You may obtain a copy of the License at                                  *
 *                                                                           *
 *     http://www.apache.org/licenses/LICENSE-2.0                            *
 *                                                                           *
 *  Unless required by applicable law or agreed to in writing, software      *
 *  distributed under the License is distributed on an "AS IS" BASIS,        *
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
 *  See the License for the specific language governing permissions and      *
 *  limitations under the License.                                           *
 *                                                                           *
 ****************************************************************************/

/*============================ INCLUDES ======================================*/

#include "hal/vsf_hal_cfg.h"

#if VSF_HAL_USE_GPIO == ENABLED

#include "hal/vsf_hal.h"
#include "hme_gpio.h"
#include "HME_MCU.h"

/*============================ MACROS ========================================*/

#ifndef VSF_HW_GPIO_CFG_MULTI_CLASS
#   define VSF_HW_GPIO_CFG_MULTI_CLASS          VSF_GPIO_CFG_MULTI_CLASS
#endif

#define VSF_GPIO_CFG_IMP_PREFIX                 vsf_hw
#define VSF_GPIO_CFG_IMP_UPCASE_PREFIX          VSF_HW

#ifndef VSF_HW_GPIO_CFG_PROTECT_LEVEL
#   define VSF_HW_GPIO_CFG_PROTECT_LEVEL        interrupt
#endif

#define __vsf_gpio_protect                      vsf_protect(VSF_HW_GPIO_CFG_PROTECT_LEVEL)
#define __vsf_gpio_unprotect                    vsf_unprotect(VSF_HW_GPIO_CFG_PROTECT_LEVEL)

/*============================ MACROFIED FUNCTIONS ===========================*/
/*============================ PROTOTYPES ====================================*/
/*============================ MACROFIED FUNCTIONS ===========================*/
/*============================ TYPES =========================================*/

// HW
typedef struct VSF_MCONNECT(VSF_GPIO_CFG_IMP_PREFIX, _gpio_t) {
#if VSF_HW_GPIO_CFG_MULTI_CLASS == ENABLED
    vsf_gpio_t                  vsf_gpio;
#endif
    GPIO_Type                  *reg;
    IRQn_Type                   irqn;
    vsf_gpio_isr_t             *isrs;
    
    uint32_t                    exti_mask;
} VSF_MCONNECT(VSF_GPIO_CFG_IMP_PREFIX, _gpio_t);
// HW end

/*============================ IMPLEMENTATION ================================*/

void VSF_MCONNECT(VSF_GPIO_CFG_IMP_PREFIX, _gpio_config_pin)(
    VSF_MCONNECT(VSF_GPIO_CFG_IMP_PREFIX, _gpio_t) *gpio_ptr,
    vsf_gpio_pin_mask_t pin_mask,
    vsf_gpio_mode_t mode
) {
    VSF_HAL_ASSERT(NULL != gpio_ptr);

    // disable hardware mode
    vsf_protect_t orig = __vsf_gpio_protect();
    gpio_ptr->reg->PORT.CTL &= ~pin_mask;
    __vsf_gpio_unprotect(orig);

    uint32_t old_exti_mask = gpio_ptr->exti_mask;
    if ((mode & VSF_GPIO_MODE_MASK) == VSF_GPIO_EXTI) {
        if (gpio_ptr->exti_mask == 0) {
            NVIC_EnableIRQ(gpio_ptr->irqn);
            // TODO: config exti irq priority
        }
        gpio_ptr->exti_mask |= pin_mask;
    } else {
        gpio_ptr->exti_mask &= ~pin_mask;
        if (gpio_ptr->exti_mask == 0) {
            NVIC_DisableIRQ(gpio_ptr->irqn);
        }
    }       
}

void VSF_MCONNECT(VSF_GPIO_CFG_IMP_PREFIX, _gpio_set_direction)(
    VSF_MCONNECT(VSF_GPIO_CFG_IMP_PREFIX, _gpio_t) *gpio_ptr,
    vsf_gpio_pin_mask_t pin_mask,
    vsf_gpio_pin_mask_t direction_mask
) {
    VSF_HAL_ASSERT(NULL != gpio_ptr);
    GPIO_Type *reg = gpio_ptr->reg;

    vsf_protect_t orig = __vsf_gpio_protect();
    reg->PORT.DDR = (reg->PORT.DDR & ~pin_mask) | (direction_mask & pin_mask);
    __vsf_gpio_unprotect(orig);
}

vsf_gpio_pin_mask_t VSF_MCONNECT(VSF_GPIO_CFG_IMP_PREFIX, _gpio_get_direction)(
    VSF_MCONNECT(VSF_GPIO_CFG_IMP_PREFIX, _gpio_t) *gpio_ptr,
    vsf_gpio_pin_mask_t pin_mask
) {
    VSF_HAL_ASSERT(NULL != gpio_ptr);
    GPIO_Type *reg = gpio_ptr->reg;

    return reg->PORT.DDR & pin_mask;
}

vsf_gpio_pin_mask_t VSF_MCONNECT(VSF_GPIO_CFG_IMP_PREFIX, _gpio_read)(
    VSF_MCONNECT(VSF_GPIO_CFG_IMP_PREFIX, _gpio_t) *gpio_ptr
) {
    VSF_HAL_ASSERT(NULL != gpio_ptr);
    GPIO_Type *reg = gpio_ptr->reg;

    return reg->EXT_PORT;
}

void VSF_MCONNECT(VSF_GPIO_CFG_IMP_PREFIX, _gpio_write)(
    VSF_MCONNECT(VSF_GPIO_CFG_IMP_PREFIX, _gpio_t) *gpio_ptr,
    vsf_gpio_pin_mask_t pin_mask,
    vsf_gpio_pin_mask_t value
) {
    VSF_HAL_ASSERT(NULL != gpio_ptr);
    GPIO_Type *reg = gpio_ptr->reg;

    vsf_protect_t orig = __vsf_gpio_protect();
    reg->PORT.DR = (reg->PORT.DR & ~pin_mask) | (value & pin_mask);
    __vsf_gpio_unprotect(orig);
}

void VSF_MCONNECT(VSF_GPIO_CFG_IMP_PREFIX, _gpio_toggle)(
    VSF_MCONNECT(VSF_GPIO_CFG_IMP_PREFIX, _gpio_t) *gpio_ptr,
    vsf_gpio_pin_mask_t pin_mask
) {
    VSF_HAL_ASSERT(NULL != gpio_ptr);
    GPIO_Type *reg = gpio_ptr->reg;

    vsf_protect_t orig = __vsf_gpio_protect();
    reg->PORT.DR ^= pin_mask;
    __vsf_gpio_unprotect(orig);
}

vsf_err_t VSF_MCONNECT(VSF_GPIO_CFG_IMP_PREFIX, _gpio_exti_irq_enable)(
    VSF_MCONNECT(VSF_GPIO_CFG_IMP_PREFIX, _gpio_t) *gpio_ptr,
    vsf_gpio_pin_mask_t pin_mask,
    vsf_arch_prio_t prio
) {
    VSF_HAL_ASSERT(NULL != gpio_ptr);
    GPIO_Type *reg = gpio_ptr->reg;

    vsf_protect_t orig = __vsf_gpio_protect();
    reg->INTEN |= pin_mask;
    __vsf_gpio_unprotect(orig);

    return VSF_ERR_NONE;
}

vsf_err_t VSF_MCONNECT(VSF_GPIO_CFG_IMP_PREFIX, _gpio_exti_irq_disable)(
    VSF_MCONNECT(VSF_GPIO_CFG_IMP_PREFIX, _gpio_t) *gpio_ptr,
    vsf_gpio_pin_mask_t pin_mask
) {
    VSF_HAL_ASSERT(NULL != gpio_ptr);
    GPIO_Type *reg = gpio_ptr->reg;

    vsf_protect_t orig = __vsf_gpio_protect();
    reg->INTEN &= ~pin_mask;
    __vsf_gpio_unprotect(orig);

    return VSF_ERR_NONE;
}

vsf_err_t VSF_MCONNECT(VSF_GPIO_CFG_IMP_PREFIX, _gpio_exti_config)(
    VSF_MCONNECT(VSF_GPIO_CFG_IMP_PREFIX, _gpio_t) *gpio_ptr,
    vsf_gpio_pin_irq_cfg_t *cfg_ptr
) {
    VSF_HAL_ASSERT(NULL != gpio_ptr);
    VSF_HAL_ASSERT(NULL != cfg_ptr);
    GPIO_Type *reg = gpio_ptr->reg;

    vsf_gpio_interrupt_mode_t mode = cfg_ptr->mode;
    uint32_t cfg_pin_mask = cfg_ptr->pin_mask;    
    VSF_HAL_ASSERT(cfg_pin_mask != 0);
    
    if (mode == VSF_GPIO_INT_MODE_RISING_FALLING) {
        return VSF_ERR_NOT_SUPPORT;
    }
    
    if ((mode == VSF_GPIO_INT_MODE_RISING) || (mode == VSF_GPIO_INT_MODE_FALLING)) {
        reg->INTTYPE_LEVEL |= cfg_pin_mask;    
    } else {
        reg->INTTYPE_LEVEL &= ~cfg_pin_mask;    
    }
    
    if ((mode == VSF_GPIO_INT_MODE_RISING) || (mode == VSF_GPIO_INT_MODE_HIGH_LEVEL)) {
        reg->INT_POLARITY |= cfg_pin_mask;    
    } else {
        reg->INT_POLARITY &= ~cfg_pin_mask;    
    }
    
    // enable debounce default
    reg->DEBOUNCE |= cfg_pin_mask; 

    while (cfg_pin_mask != 0) {
        uint32_t pin = 31 - vsf_clz32(cfg_pin_mask);
        uint32_t pin_mask = 0x01UL << pin;
        cfg_pin_mask &= ~pin_mask;

        gpio_ptr->isrs[pin] = cfg_ptr->isr;
    }
    
    return VSF_ERR_NONE;
}

static void VSF_MCONNECT(__, VSF_GPIO_CFG_IMP_PREFIX, _gpio_irqhandler)(
    VSF_MCONNECT(VSF_GPIO_CFG_IMP_PREFIX, _gpio_t) *gpio_ptr
) {
    VSF_HAL_ASSERT(NULL != gpio_ptr);
    GPIO_Type *reg = gpio_ptr->reg;

    uint32_t irq_pin_mask = reg->INTSTATUS;
    reg->PORTA_EOI = irq_pin_mask;

    while (irq_pin_mask != 0) {
        uint32_t pin = 31 - vsf_clz32(irq_pin_mask);
        uint32_t pin_mask = 0x01UL << pin;
        irq_pin_mask &= ~pin_mask;

        VSF_HAL_ASSERT(NULL != gpio_ptr->isrs[pin].handler_fn);

        gpio_ptr->isrs[pin].handler_fn(
            gpio_ptr->isrs[pin].target_ptr,
            (vsf_gpio_t *)gpio_ptr,
            pin_mask
        );
    }
}

vsf_gpio_capability_t VSF_MCONNECT(VSF_GPIO_CFG_IMP_PREFIX, _gpio_capability)(
    VSF_MCONNECT(VSF_GPIO_CFG_IMP_PREFIX, _gpio_t) *gpio_ptr
) {
    return (vsf_gpio_capability_t){
        .is_async                   = false,
        .support_output_and_set     = true,
        .support_output_and_clear   = true,
        .support_interrupt          = 1,
        .pin_count                  = 32,
        .pin_mask                   = 0xFFFFFFFF,
    };
}

/*============================ INCLUDES ======================================*/

#define VSF_GPIO_CFG_REIMPLEMENT_API_CAPABILITY             ENABLED

#define VSF_GPIO_CFG_IMP_LV0(__IDX, __HAL_OP)                                   \
    static vsf_gpio_isr_t VSF_MCONNECT(__, VSF_GPIO_CFG_IMP_PREFIX, _gpio, __IDX, _isr)[VSF_HW_GPIO_PIN_COUNT];\
    VSF_MCONNECT(VSF_GPIO_CFG_IMP_PREFIX, _gpio_t)                              \
        VSF_MCONNECT(VSF_GPIO_CFG_IMP_PREFIX, _gpio, __IDX) = {                 \
        .reg  = VSF_MCONNECT(VSF_GPIO_CFG_IMP_UPCASE_PREFIX, _GPIO, __IDX, _REG),\
        .isrs = VSF_MCONNECT(__, VSF_GPIO_CFG_IMP_PREFIX, _gpio, __IDX, _isr),  \
        .irqn = VSF_MCONNECT(VSF_GPIO_CFG_IMP_UPCASE_PREFIX, _GPIO, __IDX, _IRQN),\
       __HAL_OP                                                                 \
    };                                                                          \
    void VSF_MCONNECT(VSF_GPIO_CFG_IMP_UPCASE_PREFIX, _GPIO, __IDX,  _IRQHandler)(void)\
    {                                                                           \
        uintptr_t ctx = vsf_hal_irq_enter();                                    \
        VSF_MCONNECT(__, VSF_GPIO_CFG_IMP_PREFIX, _gpio_irqhandler)(            \
            &VSF_MCONNECT(VSF_GPIO_CFG_IMP_PREFIX, _gpio, __IDX)                \
        );                                                                      \
        vsf_hal_irq_leave(ctx);                                                 \
    }

#include "hal/driver/common/gpio/gpio_template.inc"

#endif      // VSF_HAL_USE_GPIO
